package ygo

import (
	"fmt"
	"dumpwave"
)

func alu(
		decodeAlu          chan DEAL,
		dmuxAlu            chan DMAL,
		aluMux             chan ALMU,
		aluCop0_exception  chan int32,
		ls_addr            chan uint32,
		ls_rw              chan bool, 
		ls_data_in         chan int32,
		ls_data_out        chan int32,
		aluPc              chan uint32,
		aluDecode_branch   chan uint32) {

	var addr_offset uint32

	for{
			deal :=<- decodeAlu
				switch deal.CMD{
					case ADD:
						fmt.Println("[ALU] DEAL <-- DECODE (ADD)")

						dmal:=<-dmuxAlu
						fmt.Println("[ALU] DMAL <-- DMUX")

						if (dmal.BUS1 >>31) + (dmal.BUS2 >>31) != ((dmal.BUS1+dmal.BUS2)>>31) {
							fmt.Println("[ALU] EXCEPTION --> COP0 (EX_ADES)")
							aluCop0_exception<-EX_ADES
						} else{
							fmt.Println("[ALU] EXCEPTION --> COP0 (EX_INTERRUPTION)")
							aluCop0_exception<-EX_INTERRUPTION
						}
						fmt.Printf("[ALU] ADD: %d + %d = %d\n",dmal.BUS1,dmal.BUS2,dmal.BUS1+dmal.BUS2)

						fmt.Println("[ALU] ALMU --> MUX")
						aluMux <- ALMU{EXEC_TO_REG: dmal.BUS1+dmal.BUS2, CANCELLED:false}

					case SUB:
						fmt.Println("[ALU] DEAL <-- DECODE (SUB)")

						dmal:=<-dmuxAlu
						fmt.Println("[ALU] DMAL <-- DMUX")

						if (dmal.BUS1 >>31) - (dmal.BUS2 >>31) != ((dmal.BUS1+dmal.BUS2)>>31) {
							fmt.Println("[ALU] EXCEPTION --> COP0 (EX_ADES)")
							aluCop0_exception<-EX_ADES
						} else{
							fmt.Println("[ALU] EXCEPTION --> COP0 (EX_INTERRUPTION)")
							aluCop0_exception<-EX_INTERRUPTION
						}
						fmt.Printf("[ALU] SUB: %d - %d = %d\n",dmal.BUS1,dmal.BUS2,dmal.BUS1-dmal.BUS2)

						fmt.Println("[ALU] ALMU --> MUX")
						aluMux<-ALMU{EXEC_TO_REG: dmal.BUS1-dmal.BUS2, CANCELLED:false}

					case AND:
						fmt.Println("[ALU] DEAL <-- DECODE (AND)")

						dmal:=<-dmuxAlu
						fmt.Println("[ALU] DMAL <-- DMUX")

						fmt.Println("[ALU] EXCEPTION --> COP0 (EX_INTERRUPTION)")
						aluCop0_exception<-EX_INTERRUPTION

						fmt.Printf("[ALU] AND: %d & %d = %d\n",dmal.BUS1,dmal.BUS2,dmal.BUS1 & dmal.BUS2)

						fmt.Println("[ALU] ALMU --> MUX")
						aluMux<-ALMU{EXEC_TO_REG: dmal.BUS1 & dmal.BUS2, CANCELLED:false}

					case LW:
						fmt.Println("[ALU] DEAL <-- DECODE (LW)")

						dmal:=<-dmuxAlu
						fmt.Println("[ALU] DMAL <-- DMUX")

						fmt.Println("[ALU] EXCEPTION --> COP0 (EX_INTERRUPTION)")
						aluCop0_exception<-EX_INTERRUPTION
						
						if  uint32(deal.IMMEDIATE & 0x8000) == uint32(0) { //dmal.RD == OFFSET
								addr_offset = 0x00000000
						}else {
								addr_offset = 0xFFFF0000
						}
						addr_offset = addr_offset | uint32(deal.IMMEDIATE & 0xFFFF)

						//vAddr := dmal.BUS1+int32(addr_offset)

						dumpwave.Wave(1,"req_ack_LS_ADDR")
						lsaddr:= uint32(dmal.BUS1)+addr_offset
						fmt.Println("[ALU] ls_addr --> YAM (out)")
						ls_addr <- lsaddr // uint32(dmal.BUS1)+addr_offset
						dumpwave.Wave(int(lsaddr),"cha_LS_ADDR")
						dumpwave.Wave(0,"req_ack_LS_ADDR")

						dumpwave.Wave(1,"req_ack_LS_RW")
						fmt.Println("[ALU] ls_rw --> YAM (out)")
						dumpwave.Wave(0,"cha_LS_RW")
						ls_rw <- false
						dumpwave.Wave(0,"req_ack_LS_RW")

						fmt.Println("[ALU] ls_data --> YAM (out)")
						data_out := <-ls_data_out

						fmt.Println("[ALU] ALMU --> MUX")
						aluMux <- ALMU{EXEC_TO_REG: data_out , CANCELLED:false} //RS:base BUS1, RT:rt, RD:offset BUS2

					case SW:
						fmt.Println("[ALU] DEAL <-- DECODE (SW)")

						dmal:=<-dmuxAlu
						fmt.Println("[ALU] DMAL <-- DMUX")

						fmt.Println("[ALU] EXCEPTION --> COP0 (EX_INTERRUPTION)")
						aluCop0_exception<-EX_INTERRUPTION

						if  uint32(deal.IMMEDIATE & 0x8000) == uint32(0) { //dmal.RD == OFFSET
								addr_offset = 0x00000000
						}else {
								addr_offset = 0xFFFF0000
						}
						addr_offset = addr_offset | uint32(deal.IMMEDIATE & 0xFFFF)

						fmt.Println("[ALU] ls --> YAM (in)")
						ls_addr <- uint32(dmal.BUS1)+addr_offset
						ls_rw <- true
						ls_data_in <- dmal.BUS2
						fmt.Println("[ALU] Valor guardado",dmal.BUS2)

					case BEQ:
						fmt.Println("[ALU] DEAL <-- DECODE (BEQ)")

						dmal:=<-dmuxAlu
						fmt.Println("[ALU] DMAL <-- DMUX")

						fmt.Println("[ALU] EXCEPTION --> COP0 (EX_INTERRUPTION)")
						aluCop0_exception<-EX_INTERRUPTION

						if dmal.BUS1 == dmal.BUS2 {
							if  uint32((deal.IMMEDIATE<<2) & 0x8000) == uint32(0) { //dmal.RD == OFFSET
								addr_offset = 0x00000000
							}else {
								addr_offset = 0xFFFF0000
							}
							addr_offset = addr_offset | uint32(deal.IMMEDIATE & 0xFFFF)
							fmt.Println("[ALU] addr_offset --> PC")
							aluPc <- addr_offset 
						}

					case ADDI:
						fmt.Println("[ALU] DEAL <-- DECODE (ADDI)")

						dmal:=<-dmuxAlu
						fmt.Println("[ALU] DMAL <-- DMUX")

						if  uint32((deal.IMMEDIATE<<2) & 0x8000) == uint32(0) { //dmal.RD == OFFSET
							addr_offset = 0x00000000
						} else {
							addr_offset = 0xFFFF0000
						}
						addr_offset = addr_offset | uint32(deal.IMMEDIATE & 0xFFFF)

						if (dmal.BUS1 >>31) + int32(addr_offset >>31) != ((dmal.BUS1+int32(addr_offset))>>31) {
							aluCop0_exception<-EX_ADES
						} else {
							aluCop0_exception<-EX_INTERRUPTION
						}
						fmt.Printf("[ALU] ADD: %d + %d = %d\n",dmal.BUS1,addr_offset,dmal.BUS1+int32(addr_offset))

						fmt.Println("[ALU] ALMU --> MUX")
						aluMux<-ALMU{EXEC_TO_REG: dmal.BUS1+int32(addr_offset), CANCELLED:false}
				}
		}
	
	
}
